/**
 * @author      Jorge Blasco Al�s <jbalis@inf.uc3m.es>
 * @author		Guillermo Suarez de Tangil Rotaeche <gtangil@pa.uc3m.es>
 * @version     1.0             
 */
package es.uc3m.setichat.appengine.servlets;

import java.io.IOException;
import java.util.logging.Logger;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.xmpp.Message;
import com.google.appengine.api.xmpp.XMPPService;
import com.google.appengine.api.xmpp.XMPPServiceFactory;

import es.uc3m.setichat.constants.ErrorConstants;
import es.uc3m.setichat.constants.GeneralConstants;
import es.uc3m.setichat.constants.MessageTypeConstants;
import es.uc3m.setichat.utils.ServerActions;
import es.uc3m.setichat.utils.setichatxmlschema.SetiChatMessage;
import es.uc3m.setichat.utils.setichatxmlschema.SetiChatMessage.Header;
import es.uc3m.setichat.utils.setichatxmlschema.SetiChatMessageFactory;

/**
 * Handler class for all XMPP messages.
 */
public class XmppReceiverServlet extends HttpServlet {

	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 2481475683995025129L;

	/**
	 * XMPP service from Google App Engine
	 */
	public static final XMPPService xmppService = XMPPServiceFactory
			.getXMPPService();

	/**
	 * Logger used to debug the app
	 */
	public static final Logger log = Logger.getLogger(XmppReceiverServlet.class
			.getName());
	/**
	 * SeTiChatMessage Factory to manager SeTiChatMessage
	 * */
	public static SetiChatMessageFactory setiChatMessageFactory = new SetiChatMessageFactory();
	/**
	 * Datastore Service from Google App Engine
	 * */
	public static DatastoreService datastore = DatastoreServiceFactory
			.getDatastoreService();

	/**
	 * Method that receives incoming chats
	 * 
	 * @param req
	 *            an HTTP request that includes the XMPP message
	 * @param resp
	 *            an HTTP response that should include the response. It is left
	 *            blank, as answers are created through the XMPP service.
	 */
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {
		Message msg = null;
		SetiChatMessage scMsg = null;
		String scMsgResponse = new String();
		boolean isNotNumberType = false;
		boolean isSCMsgFormat = true;
		boolean idsSourceNotMatch = false;
		boolean isUnkownError = false;
		// Extract body from message
		msg = xmppService.parseMessage(req);
		try {
			String scMessageXml = msg.getBody();
			// log.warning(scMessageXml);
			// Parse body to setiChatMessage
			scMsg = setiChatMessageFactory.createSetiChatMessage(scMessageXml);
			int typeSetiChatMessage = -1;

			if (scMsg.getHeader() != null) {
				// Extract type from setiChatMessage
				typeSetiChatMessage = Integer.parseInt(scMsg.getHeader()
						.getType());
			} else {
				isSCMsgFormat = false;
			}
			// Id of user of sent signup message should not be at data store
			if (typeSetiChatMessage != MessageTypeConstants.TYPE_SIGNUP) {
				idsSourceNotMatch = ServerActions.idsSourceNotMatch(msg, scMsg);
			}
			if (!idsSourceNotMatch && isSCMsgFormat) {
				switch (typeSetiChatMessage) {
				case MessageTypeConstants.TYPE_SIGNUP:
					log.warning("Signup message received");
					String emailSource = ServerActions.getEmail(msg
							.getFromJid());
					scMsgResponse = ServerActions.doSignUp(scMsg, emailSource);
					if (ServerActions.isSrcAvailable(msg)) {
						ServerActions.replyToMessage(msg, scMsgResponse);
						log.warning("Response message sent");
					} else {
						SetiChatMessage scMsgRsp = setiChatMessageFactory
								.createSetiChatMessage(scMsgResponse);
						ServerActions.doStoreChatMessage(scMsgRsp);
						log.warning("Response message stored");
					}
					break;
				case MessageTypeConstants.TYPE_CONTACTREQUEST:
					log.warning("Contact request message received");
					scMsgResponse = ServerActions.doContactRequest(scMsg);
					if (ServerActions.isSrcAvailable(msg)) {
						ServerActions.replyToMessage(msg, scMsgResponse);
						log.warning("Response message sent");
					} else {
						SetiChatMessage scMsgRsp = setiChatMessageFactory
								.createSetiChatMessage(scMsgResponse);
						ServerActions.doStoreChatMessage(scMsgRsp);
						log.warning("Response message stored");
					}
					break;
				case MessageTypeConstants.TYPE_CONNECTION:
					log.warning("Connection message received");
					scMsgResponse = ServerActions.doConnection(scMsg);
					if (ServerActions.isSrcAvailable(msg)) {
						ServerActions.replyToMessage(msg, scMsgResponse);
						log.warning("Response message sent");
					} else {
						SetiChatMessage scMsgRsp = setiChatMessageFactory
								.createSetiChatMessage(scMsgResponse);
						ServerActions.doStoreChatMessage(scMsgRsp);
						log.warning("Response message stored");
					}
					int nPendingMessage = ServerActions
							.doSendPendingMessage(scMsg.getHeader()
									.getIdSource());
					log.warning("Send " + nPendingMessage + " pending messages");
					break;
				case MessageTypeConstants.TYPE_CHATMESSAGE:
					log.warning("Chat message received");
					scMsgResponse = ServerActions.doChatMessage(scMsg);
					if (ServerActions.isSrcAvailable(msg)) {
						ServerActions.replyToMessage(msg, scMsgResponse);
						log.warning("Response message sent");
					} else {
						SetiChatMessage scMsgRsp = setiChatMessageFactory
								.createSetiChatMessage(scMsgResponse);
						ServerActions.doStoreChatMessage(scMsgRsp);
						log.warning("Response message stored");
					}
					if (ServerActions.isDstAvailable(scMsg)) {
						ServerActions.sendToMessage(scMsg);
						log.warning("Seti Chat message send to recipient");
					} else {
						ServerActions.doStoreChatMessage(scMsg);
						log.warning("Seti Chat message stored");
					}
					break;
				case MessageTypeConstants.TYPE_UPLOAD:
					log.warning("Upload message received");
					scMsgResponse = ServerActions.doUploadMessage(scMsg);
					if (ServerActions.isSrcAvailable(msg)) {
						ServerActions.replyToMessage(msg, scMsgResponse);
						log.warning("Send message: " + scMsgResponse);
					} else {
						SetiChatMessage scMsgRsp = setiChatMessageFactory
								.createSetiChatMessage(scMsgResponse);
						ServerActions.doStoreChatMessage(scMsgRsp);
						log.warning("Response message stored");
					}
					break;
				case MessageTypeConstants.TYPE_CERTIFICATEREQUEST:
					log.warning("Certificate request message received");
					scMsgResponse = ServerActions.doCertificateRequest(scMsg);
					if (ServerActions.isSrcAvailable(msg)) {
						ServerActions.replyToMessage(msg, scMsgResponse);
						log.warning("Send message: " + scMsgResponse);
					} else {
						SetiChatMessage scMsgRsp = setiChatMessageFactory
								.createSetiChatMessage(scMsgResponse);
						ServerActions.doStoreChatMessage(scMsgRsp);
						log.warning("Response message stored");
					}
					break;
				default:
					isNotNumberType = true;
				}
			}
		} catch (NumberFormatException e) {
			isNotNumberType = true;
		} catch (CreateSetiChatMessageException e) {
			log.warning(ErrorConstants.ERROR_MESSAGE_WRONG_FORMAT);
			isSCMsgFormat = false;
		} catch (NullPointerException e) {
			log.warning("Probably: "
					+ ErrorConstants.ERROR_MESSAGE_WRONG_FORMAT);
			isSCMsgFormat = false;
		} finally {
			if (isUnkownError) {
				scMsgResponse = ServerActions.doErrorMessage(scMsg,
						ErrorConstants.ERROR_CODE_UNKNOWN_ERROR,
						ErrorConstants.ERROR_MESSAGE_UNKNOWN_ERROR);

				log.warning(ErrorConstants.ERROR_MESSAGE_UNKNOWN_ERROR);
				ServerActions.replyToMessage(msg, scMsgResponse);
			} else {
				if (isNotNumberType) {
					scMsgResponse = ServerActions.doErrorMessage(scMsg,
							ErrorConstants.ERROR_CODE_TYPE_NOT_SUPPORTED,
							ErrorConstants.ERROR_MESSAGE_TYPE_NOT_SUPPORTED);

					log.warning(ErrorConstants.ERROR_MESSAGE_TYPE_NOT_SUPPORTED);
					ServerActions.replyToMessage(msg, scMsgResponse);
				}
				if (!isSCMsgFormat) {
					Header header = setiChatMessageFactory.newInstanceHeader(
							GeneralConstants.SELF,
							msg.getRecipientJids()[0].toString(),
							ErrorConstants.ERROR_IDMESSAGE,
							String.valueOf(MessageTypeConstants.TYPE_RESPONSE),
							false, false);

					scMsgResponse = ServerActions.doErrorMessage(header,
							ErrorConstants.ERROR_CODE_WRONG_FORMAT,
							ErrorConstants.ERROR_MESSAGE_WRONG_FORMAT);

					log.warning(ErrorConstants.ERROR_MESSAGE_WRONG_FORMAT);
					ServerActions.replyToMessage(msg, scMsgResponse);
				}
				if (idsSourceNotMatch) {
					scMsgResponse = ServerActions
							.doErrorMessage(
									scMsg,
									ErrorConstants.ERROR_CODE_SOURCES_IDS_DO_NOT_MATCH,
									ErrorConstants.ERROR_MESSAGE_SOURCES_IDS_DO_NOT_MATCH);

					log.warning(ErrorConstants.ERROR_MESSAGE_SOURCES_IDS_DO_NOT_MATCH);
					ServerActions.replyToMessage(msg, scMsgResponse);
				}
			}
		}

	}
}
